﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.IO;

namespace FileUploader
{
    public partial class MainPage : UserControl
    {
        /// <summary>
        /// Constructor that initializes the uploader
        /// </summary>
        public MainPage()
        {
            InitializeComponent();
        }
        /// <summary>
        /// Check if params present, and decorate to suite
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void LayoutRoot_Loaded(object sender, RoutedEventArgs e)
        {
            if (App.Current.Resources["EndPoint"] == null)
            {
                Deactivate();
                MessageBox.Show("End point has not been initialized");
            }
            else
            {
                EndPoint = (string)App.Current.Resources["EndPoint"];
                try
                {
                    System.Uri uri = new Uri(EndPoint);
                }
                catch
                {
                    Deactivate();
                    MessageBox.Show("The spacified end point is invalid");
                }
            }
            if (App.Current.Resources["GID"] != null)
            {
                GID = (string)App.Current.Resources["GID"];
            }
            else
            {
                Deactivate();
                MessageBox.Show("GID has not been specified");
            }
        }
        /// <summary>
        /// Global endpoint
        /// </summary>
        string EndPoint = "";
        /// <summary>
        /// GUID of the Uploader
        /// </summary>
        string GID = "";
        /// <summary>
        /// Contains the content length
        /// </summary>
        long ContentLength = 0;
        /// <summary>
        /// Current position of reading marker
        /// </summary>
        long CurrentPosition = 0;
        /// <summary>
        /// Content of the file
        /// </summary>
        /// <returns></returns>
        FileStream FileContent;
        /// <summary>
        /// FileName
        /// </summary>
        string FileName = "";
        /// <summary>
        /// Choose a file to upload
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void FilePicker_Click(object sender, RoutedEventArgs e)
        {
            OpenFileDialog OFD = new OpenFileDialog();
            OFD.Multiselect = false;

            bool? dr = OFD.ShowDialog();
            if (dr != true)
            { return; }
            Deactivate();
            UploadFile(OFD.File.Name, OFD.File.OpenRead());
        }
        /// <summary>
        /// Upload a file
        /// </summary>
        /// <param name="FileName"></param>
        /// <param name="Content"></param>
        private void UploadFile(string Filename, FileStream Content)
        {
            FileName = Filename;
            CurrentPosition = 0;
            ContentLength = Content.Length;
            FileContent = Content;
            ProcceedNext();
        }
        /// <summary>
        /// Start posting some bytes to the server
        /// </summary>
        /// <param name="Data"></param>
        /// <param name="Length"></param>
        private void PostBytes(byte[] Data, int Length)
        {
            UriBuilder ub = new UriBuilder(EndPoint);
            HttpWebRequest hwreq = (HttpWebRequest)WebRequest.Create(ub.Uri);
            hwreq.Headers["filename"] = FileName;
            hwreq.Headers["gid"] = GID;
            hwreq.Headers["position"] = CurrentPosition.ToString();
            hwreq.Headers["length"] = ContentLength.ToString();
            hwreq.Method = "POST";

            ObjState os = new ObjState();
            os.Object = hwreq;
            os.State1 = Data;
            os.State2 = Length;

            hwreq.BeginGetRequestStream(new AsyncCallback(ReqStreamFound), os);
        }
        /// <summary>
        /// Request state log
        /// </summary>
        private class ObjState
        {
            public Object Object;
            public Object State1;
            public Object State2;
        }
        /// <summary>
        /// Asyncronus RequestStream Found
        /// </summary>
        /// <param name="iRes"></param>
        private void ReqStreamFound(IAsyncResult iRes)
        {
            ObjState os = (ObjState)iRes.AsyncState;
            HttpWebRequest hwreq = (HttpWebRequest)os.Object;
            byte[] Data = (byte[])os.State1;
            int Length = (int)os.State2;
            Stream Output = hwreq.EndGetRequestStream(iRes);

            Output.Write(Data, 0, Length);
            Output.Flush();
            Output.Close();
            CurrentPosition += Length;

            hwreq.BeginGetResponse(new AsyncCallback(ResFound), hwreq);
        }
        /// <summary>
        /// Asyncronus response found
        /// </summary>
        /// <param name="iRes"></param>
        private void ResFound(IAsyncResult iRes)
        {
            HttpWebRequest hwreq = (HttpWebRequest)iRes.AsyncState;
            HttpWebResponse hwresp = (HttpWebResponse)hwreq.EndGetResponse(iRes);
            string result = "";
            using (StreamReader sr = new StreamReader(hwresp.GetResponseStream()))
            {
                result = sr.ReadToEnd();
            }
            hwresp.Close();
            ProcceedNext();
        }
        /// <summary>
        /// A delegate with one input
        /// </summary>
        /// <param name="Result"></param>
        delegate void oneinp(string Result);
        /// <summary>
        /// Send next chunk
        /// </summary>
        private void ProcceedNext()
        {
            decimal Progress = (decimal)(((decimal)CurrentPosition / (decimal)ContentLength) * 100);
            Progress = decimal.Round(Progress, 2);
            this.Dispatcher.BeginInvoke(new oneinp(UpdateStat), new object[] { Progress.ToString() });

            if (CurrentPosition == ContentLength)
            {
                this.Dispatcher.BeginInvoke(new oneinp(Activate), new object[] { Progress.ToString() });
                FileContent.Close();
                return;
            }
            //Else continue posting data
            byte[] Data = new byte[256000];
            int Count = FileContent.Read(Data, 0, Data.Length);
            if (Count == 0)
            {
                this.Dispatcher.BeginInvoke(new oneinp(Activate), new object[] { Progress.ToString() });
                FileContent.Close();
                return;
            }

            PostBytes(Data, Count);
        }

        private void UpdateStat(string Result)
        {
            FilePicker.Content = Result + "%";
        }
        /// <summary>
        /// Activate the uploader
        /// </summary>
        private void Activate(string result)
        {
            FilePicker.Content = "Choose File";
            FilePicker.IsEnabled = true;
            FilePicker.Foreground = new SolidColorBrush(Colors.Blue);
            FilePicker.Cursor = Cursors.Hand;
        }
        /// <summary>
        /// Deactivate the Uploader
        /// </summary>
        private void Deactivate()
        {
            FilePicker.IsEnabled = false;
            FilePicker.Foreground = new SolidColorBrush(Colors.Gray);
            FilePicker.Cursor = Cursors.Arrow;
        }
    }
}
